package com.xianggu.nio.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 * NIO服务端实现（多路复用版本）
 * 核心流程：
 * 1. 初始化Selector和ServerSocketChannel
 * 2. 注册ACCEPT事件
 * 3. 事件循环处理（ACCEPT/READ）
 * 4. 资源清理
 */
public class NioServer {
    private static final int DEFAULT_PORT = 8080;
    private Selector selector;       // 事件选择器（多路复用器）
    private ServerSocketChannel ssc; // 服务端监听通道
    private volatile boolean stop;  // 服务停止标志

    public static void main(String[] args) throws IOException {
        new NioServer().start(DEFAULT_PORT);
    }

    /**
     * 服务启动入口
     * @param port 监听端口
     */
    public void start(int port) throws IOException {
        // ================= 1. 初始化组件 =================
        // 创建Selector实例（事件监听器）
        selector = Selector.open();  
        
        // 创建ServerSocketChannel并配置为非阻塞模式
        ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false); // 必须设置为非阻塞才能配合Selector使用
        
        // 绑定端口并设置等待队列长度为1024
        ssc.socket().bind(new InetSocketAddress(port), 1024);
        
        // ================= 2. 注册事件监听 =================
        // 将ServerSocketChannel注册到Selector，监听ACCEPT事件（新连接到达事件）
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("Server started on port: " + port);

        // ================= 3. 事件处理循环 =================
        while (!stop) {
            // 阻塞等待就绪的通道，超时1秒（防止线程永久阻塞）
            int readyChannels = selector.select(1000);
            
            // 无就绪通道时跳过本次处理
            if(readyChannels == 0) continue;  
            
            // 获取就绪通道的SelectionKey集合
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
            
            // 遍历处理所有就绪事件
            while(keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();
                keyIterator.remove(); // 必须移除已处理的key
                
                // 验证key有效性（可能被其他线程取消）
                if(!key.isValid()) continue;  
                
                // 处理ACCEPT事件（新连接到达）
                if(key.isAcceptable()) {
                    handleAccept(key);
                }
                // 处理READ事件（数据可读）
                else if(key.isReadable()) {
                    handleRead(key);
                }
            }
        }
        
        // ================= 4. 资源清理 =================
        // 关闭Selector会同时关闭已注册的Channel
        if(selector != null) {
            try { selector.close(); } 
            catch (IOException e) { e.printStackTrace(); }
        }
    }

    /**
     * 处理新连接接入
     * @param key 包含ServerSocketChannel的SelectionKey
     */
    private void handleAccept(SelectionKey key) throws IOException {
        // 获取触发事件的ServerSocketChannel
        ServerSocketChannel serverChannel = (ServerSocketChannel)key.channel();
        
        // 接受客户端连接（不会阻塞，因为事件已就绪）
        SocketChannel clientChannel = serverChannel.accept();
        clientChannel.configureBlocking(false); // 配置非阻塞模式
        
        // 注册READ事件监听，并附加接收缓冲区
        clientChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
        System.out.println("New client connected: " 
            + clientChannel.getRemoteAddress());
    }

    /**
     * 处理数据读取
     * @param key 包含SocketChannel的SelectionKey
     */
    private void handleRead(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel)key.channel();
        ByteBuffer buffer = (ByteBuffer)key.attachment(); // 获取关联的缓冲区
        
        // 清空缓冲区准备读取
        buffer.clear(); 
        
        // 读取数据到缓冲区（返回读取字节数，-1表示连接关闭）
        int bytesRead = channel.read(buffer);
        
        if(bytesRead == -1) { 
            // 客户端主动关闭连接
            System.out.println("Client disconnected: " + channel.getRemoteAddress());
            channel.close();
            return;
        }
        
        // 切换缓冲区为读模式
        buffer.flip(); 
        
        // 解码并处理数据（示例简单打印）
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes);
        String received = new String(bytes, "UTF-8");
        System.out.println("Received: " + received);
        
        // 示例响应（实际业务处理应在此处实现）
        String response = "Server response: " + received;
        ByteBuffer writeBuffer = ByteBuffer.wrap(response.getBytes());
        channel.write(writeBuffer);
    }

    /**
     * 停止服务
     */
    public void stop() {
        this.stop = true;
    }
}